package org.example;

import org.opencv.core.*;
import org.opencv.features2d.*;
import org.opencv.calib3d.Calib3d;
import org.opencv.imgproc.Imgproc;

/**
 * 图片对齐
 */
public class ImageAligner {
    static {
        //加载 OpenCV 的本地库
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }

    /**
     * 使用ORB特征和BFMatcher匹配来对齐两幅图像
     * 该方法首先使用ORB特征检测器在参考图像和当前图像中找到关键点和描述符，
     * 然后使用BFMatcher进行特征匹配，最后通过计算单应性矩阵来对齐图像
     *
     * @param refImage 参考图像，用于对齐的目标
     * @param currentImage 当前图像，需要对齐的图像
     * @return 对齐后的图像
     */
    public static Mat alignImages(Mat refImage, Mat currentImage) {
        // 创建ORB特征检测器
        ORB orb = ORB.create();
        // 初始化关键点和描述符的存储容器
        MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
        MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
        Mat descriptors1 = new Mat();
        Mat descriptors2 = new Mat();

        // 在参考图像和当前图像中检测关键点并计算描述符
        orb.detectAndCompute(refImage, new Mat(), keypoints1, descriptors1);
        orb.detectAndCompute(currentImage, new Mat(), keypoints2, descriptors2);

        // 创建BFMatcher匹配器，使用HAMMING距离进行匹配
        BFMatcher matcher = BFMatcher.create(BFMatcher.BRUTEFORCE_HAMMING, true);
        MatOfDMatch matches = new MatOfDMatch();
        // 进行特征匹配
        matcher.match(descriptors1, descriptors2, matches);

        // 初始化匹配点的坐标存储容器
        MatOfPoint2f srcPoints = new MatOfPoint2f();
        MatOfPoint2f dstPoints = new MatOfPoint2f();
        // 遍历所有匹配结果，获取匹配点的坐标
        for (DMatch match : matches.toArray()) {
            srcPoints.push_back(new MatOfPoint2f(keypoints1.toArray()[match.queryIdx].pt));
            dstPoints.push_back(new MatOfPoint2f(keypoints2.toArray()[match.trainIdx].pt));
        }

        // 使用RANSAC算法计算单应性矩阵
        Mat homography = Calib3d.findHomography(srcPoints, dstPoints, Calib3d.RANSAC, 5.0);
        Mat alignedImage = new Mat();
        // 应用单应性矩阵对当前图像进行透视变换，实现图像对齐
        Imgproc.warpPerspective(currentImage, alignedImage, homography, refImage.size());
        return alignedImage;
    }
}
